/*
 * Copyright (c) 2007, 2008 Johannes Krampf <Johannes.Krampf@htw-dresden.de>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include "bstring/bstrlib.h"
#include "general.h"
#include "database.h"
#include "language.h"
#include "conjugation.h"

void efree(char* string) {
	if ((NULL != string) && (0 != strcmp("", string))) {
		free(string);
	}
}

int FormExists(char* form, struct language* lang) {
	int i;
	
	if (0 == FormIsUnique(form, lang)) {
		return 0;
	}
	
	if (0 == FormIsCombine(form, lang)) {
		return 0;
	}

	for (i=0;i<lang->numtenses;i++) {
		if (0 == strcmp(form, lang->tenses[i])) {
			return 0;
		}
	}
	
	return 1;
}
int FormIsUnique(char* form, struct language* lang) {
	int i;
	
	for (i=0;i<lang->numuniques;i++) {
		if (0 == strcmp(form, lang->uniques[i])) {
			return 0;
		}
	}
	
	return 1;
}

int FormIsCombine(char* form, struct language* lang) {
	int i;
	
	for (i=0;i<lang->numcombines;i++) {
		if (0 == strcmp(form, lang->combines[i])) {
			return 0;
		}
	}
		
	return 1;
}

int VerbExists(char* verb, struct language* lang) {
	int i;
	
	for (i=0;i<lang->numverbs;i++) {
		if (0 == strcmp(verb, lang->verbs[i])) {
			return 0;
		}
	}

	return 1;
}

char* GetRules(char* verb, bstring list) {
	const int slen = strlen(verb);
	struct bstrList* elements = NULL;
	char* retval;
	int i;
	
	if (NULL == list) {
		fprintf(stderr, "GetRules: Leere Liste uebergeben.\n");
		return "";
	}
	
	elements = bsplit(list, '|');
	if (NULL == elements) {
		MemError();
		return "";
	}
		
	for (i=0;i<elements->qty;i++) {
		if (slen < elements->entry[i]->slen) {
			if (0 == strncmp(verb, (char *)elements->entry[i]->data, slen)) {
				retval = calloc(elements->entry[i]->slen-slen, sizeof(char));
				strcpy(retval, (char *)elements->entry[i]->data+slen+1);
				bstrListDestroy(elements);
				return retval;
			}
		}
	}

	bstrListDestroy(elements);
	return "";
}

char* CheckSuffixes(char* verb, struct language* lang) {
	int i, tempint;
	int maxlen = 0, all = 0;
	char* retval = "";
	const int slen = strlen(verb);
	
	if (NULL == lang) {
		fprintf(stderr, "CheckSuffixes: Ungueltiger Zeiger auf Sprache\n");
		return "";
	}
	
	for (i=0;i<lang->numsuffixes;i++) {
		tempint = strlen(lang->suffixes[i]);
		if(slen >= tempint) {
			if ((3 == tempint) && (0 == strncmp("ALL", lang->suffixes[i], 3))) {
				all = 1;
			}
			if (0 == strncmp(verb+(slen-tempint), lang->suffixes[i], tempint)) {
				if (tempint > maxlen) {
					maxlen = tempint;
					retval = lang->suffixes[i];
				}
			}
		}
	}
	
	if((0 == maxlen) && (1 == all)) {
		return "ALL";
	}
	
	return retval;
}

char* CreateForm(char* verb, char* form, int pronoun, char* rules, struct language* lang) {
	char* retval = NULL;
	char buffer[128] = "";
	char buffer2[128] = "";
	char rulesfile[128] = "";
	char* temp = NULL;
	char* temp2 = NULL;
	char* stem = NULL;
	int tempint;
	int conjform = 0;

	if ((NULL == rules) || (0 == strcmp("", rules))) {
		return "";
	}

	sprintf(rulesfile, "%s-%s", lang->name, rules);
	
	if ((0 == FormIsUnique(form, lang)) || (0 == FormIsCombine(form, lang))) {
		strcpy(buffer, form);
	}else {
		sprintf(buffer, "%s %d", form, pronoun);
	}

	retval = GetRules(buffer, GetFileContent(rulesfile));
	if (0 == strcmp("", retval)) {
		strcpy(buffer2, rulesfile);
		do {
			efree(temp);
			efree(retval);
			temp = GetRules("fall", GetFileContent(buffer2));
			sprintf(buffer2, "%s-%s", lang->name, temp);
			retval = GetRules(buffer, GetFileContent(buffer2));
		} while ((0 == strcmp("", retval)) && (0 != strcmp("", temp)));
		efree(temp);
	}

/* inpp !inpr %vaus %ppas */
	if (0 == FormIsCombine(form, lang)) {
		temp = strtok(retval," ");
		buffer[0] = '\0';
		
		while (NULL != temp) {
			if (strlen(temp) > 0) {
				if ('!' == temp[0]) {
					if (0 == conjform) {
						strcpy(buffer, temp+1);
						conjform = 1;
					}else {
						fprintf(stderr, "CreateForm: Momentan nur eine konjugierte Form fuer 'combine' erlaubt.\n");
					}
				}else if ('%' == temp[0])  {
					if (1 == conjform) {
						temp2 = CreateForm(verb, temp+1, pronoun, rules, lang);
						temp = ConjugateVerb(temp2, buffer, pronoun, lang);
						efree(temp2);
						temp2 = NULL;
						strcpy(buffer, temp);
						efree(temp);
						temp = NULL;
						conjform = 2;
					}else {
						strcat(buffer, " ");
						temp = CreateForm(verb, temp+1, pronoun, rules, lang);
						strcat(buffer, temp);
						efree(temp);
						temp = NULL;
					}
				}else if ('+' == temp[0])  {
					strcat(buffer, temp+1);
					temp = NULL;
				}
			}
			temp = strtok(NULL," ");
		}
		efree(retval);
		retval = calloc(strlen(buffer)+1, sizeof(char));
		if (NULL == retval) {
			MemError();
			return "";
		}
		strcpy(retval, buffer);
		
		return retval;
	}
	
	/* unregelmäßige Form, keine Konjugation nötig */
	if (strlen(retval) > 0) {
		if('+' != retval[0]) {
			return retval;
		}
	}else {
		efree(retval);
		temp = GetRules("fall", GetFileContent(rulesfile));
		retval = CreateForm(verb, form, pronoun, temp, lang);
		efree(temp);
		return retval;
	}

	/* Konjugation Stamm + Endung */
	stem = GetRules("stem", GetFileContent(rulesfile));

	if (0 == strcmp("", stem)) {
		strcpy(buffer, rulesfile);
		do {
			efree(stem);
			temp = GetRules("fall", GetFileContent(buffer));
			sprintf(buffer, "%s-%s", lang->name, temp);
			stem = GetRules("stem", GetFileContent(buffer));
		} while ((0 == strcmp("", retval)) && (0 != strcmp("", temp)));
		efree(temp);
	}

	if (strlen(stem) > 0) {
		if('-' == stem[0]) {
			tempint = strlen(verb)-strlen(stem+1);
			if (0 == strcmp(verb+tempint, stem+1)) {
				strcpy(stem, verb);
				stem[tempint] = '\0';
			}
		}
	}

	strcpy(buffer, stem);
	strcat(buffer, retval+1);

	efree(retval);
	efree(stem);
	retval = calloc(strlen(buffer)+1, sizeof(char));
	if (NULL == retval) {
		MemError();
		return "";
	}
	strcpy(retval, buffer);
	
	return retval;
}

char* ConjugateVerb(char* verb, char* form, int pronoun, struct language* lang) {
	char* rules = NULL;
	char* temp = NULL;
	char buffer[128];
	
	if (NULL == lang) {
		fprintf(stderr, "ConjugateVerb: Ungueltiger Zeiger auf Sprache\n");
		return "";
	}
	
	/* angeforderte Form vorhanden */
	if (0 != FormExists(form, lang)) {
		fprintf(stderr, "ConjugateVerb: Form %s nicht gefunden fuer %s.\n", form, lang->name);
		return "";
	}
	
	/* gültiges Prononem */
	if ((pronoun > lang->numpronouns) || (0 > pronoun)) {
		fprintf(stderr, "ConjugateVerb: Pronomen %d nicht gueltig fuer %s\n", pronoun, lang->name);
		return "";
	}
	
	if (0 == VerbExists(verb, lang)) {
		buffer[0] = '\0';
		strcpy(buffer, lang->name);
		strcat(buffer, "_verbs");

		rules = GetRules(verb, GetFileContent(buffer));
	}else {
		temp = CheckSuffixes(verb, lang);
		if (0 != strcmp("", temp)) {
			buffer[0] = '\0';
			strcpy(buffer, lang->name);
			strcat(buffer, "_suffixes");

			rules = GetRules(temp, GetFileContent(buffer));
			
		}else {
			return "";
		}
	}
	
	strcpy(buffer, rules);
	efree(rules);
	
	return CreateForm(verb, form, pronoun, buffer, lang);
}
